Load packages

library(tidyverse)
library(gridExtra)
library(cowplot)
library(viridis)
library(ggridges)
library(ggstance)
library(treeio)
library(ggtree)
library(tidytree)

Load and combine data

studies <- read_csv("../data/studies_gsheet.csv")
species <- read_csv("../data/species_gsheet.csv")
fulltree <- read.nexus("../data/consensusTree_10kTrees_298Primates_V3.nex")
refs <- read_csv("../data/ref_nodes.csv")
data <- studies %>% 
  left_join(species, by = c("species" = "species_file")) %>% 
  rename(label = species_tree, label2 = updated_nomenclature, num = n) %>% 
  mutate(label2 = ifelse(is.na(label2), label, label2),
         label2 = fct_recode(label2,
                             "Sapajus_spp." = "Sapajus_apella",
                             "Hoolock_leuconedys*" = "Hoolock_hoolock",
                             "Plecturocebus_cupreus*" = "Plecturocebus_moloch",
                             "Callicebus_nigrifrons*" = "Callicebus_personatus",
                             "Eulemur_rufifrons*" = "Eulemur_rufus")) %>% 
  select(label, label2, studyID, species, site, num)
data2 <- data %>%
  group_by(label, label2, species) %>% 
  summarise(Nsites = n_distinct(site), Nstudies = n_distinct(studyID)) %>% 
  mutate(div_score = Nsites/Nstudies)
species <- species %>% 
  mutate(updated_nomenclature = fct_recode(updated_nomenclature,
                                           "Sapajus_spp." = "Sapajus_apella",
                                           "Hoolock_leuconedys*" = "Hoolock_hoolock",
                                           "Plecturocebus_cupreus*" = "Plecturocebus_moloch",
                                           "Callicebus_nigrifrons*" = "Callicebus_personatus",
                                           "Eulemur_rufifrons*" = "Eulemur_rufus"))
# turn tree into tidy dataframe
tree2 <- fulltree %>% 
  drop.tip(c("Pan_troglodytes_schweinfurthii", "Pan_troglodytes_troglodytes",
             "Pan_troglodytes_vellerosus", "Pongo_pygmaeus", "Cercopithecus_cephus_cephus",
             "Cercopithecus_erythrogaster_erythrogaster", "Eulemur_fulvus_mayottensis",
             "Hapalemur_griseus_griseus", "Microcebus_lokobensis", "Lepilemur_mitsinjoensis",
             "Gorilla_gorilla_graueri", "Cebus_xanthosternos")) %>%
  as_tibble

inner_nodes <- c(287:296, 305:306, 311:317, 319:323, 325, 327:344, 347:349, 353:355, 357, 373:381, 386, 390:393, 395:401, 403:412, 420:423, 425:429, 435:439, 443:445, 447:451, 454:461, 463:467, 469:473, 477:488, 500, 518:519, 523:525, 528:532, 538:540, 546:548, 551, 553)

tree3 <- tree2 %>% 
  mutate(label = fct_recode(label, 
                            "Pongo_spp." = "Pongo_abelii",
                            "Pan_troglodytes" = "Pan_troglodytes_verus")) %>% 
  left_join(data2) %>% 
  left_join(species, by = c("label" = "species_tree")) %>% 
  select(-label, -label2, -species_file) %>%
  rename(label = updated_nomenclature) %>%
  mutate(
    hasN = ifelse(is.na(Nstudies), .1, .5), # used to size branches + color the tip labels
    hasN2 = ifelse(is.na(Nstudies) & !(node %in% inner_nodes), 0, .5), # used to color branches
    label = str_replace_all(label, "_", " "),
    # label = fct_recode(label, "Sapajus spp." = "Sapajus apella", "Hoolock hoolock*" = "Hoolock hoolock"),
    label = ifelse(is.na(label) & species == "orangutan", "Pongo spp.", 
              ifelse(is.na(label) & species == "chimpanzee", "Pan troglodytes", label))) %>% 
  left_join(refs) %>% 
  groupClade(refs$node[-1]) %>% 
  mutate(group = fct_recode(group, "2" = "1"))
Joining, by = "label"
Column `label` joining factor and character vector, coercing into character vectorJoining, by = "node"
# turn back into tree
tree4 <- as.treedata(tree3)

Circular tree of the 10ktree primates

cols <- viridis(4, end = .9)
p <- ggtree(tree4, aes(alpha = hasN2), layout = "circular") + # size = hasN, 
  # highlight clades with background colors
  geom_hilight(node = 474, fill = cols[1], alpha = .3) +
  geom_hilight(node = 477, fill = cols[1], alpha = .3) +
  geom_hilight(node = 411, fill = cols[2], alpha = .3) +
  geom_hilight(node = 291, fill = cols[3], alpha = .3) +
  geom_hilight(node = 395, fill = cols[4], alpha = .3) +
  # plot tree again to be on top of the highlights
  geom_tree() +
  # root
  geom_rootpoint(size = 1) +
  # tips
  geom_tippoint(aes(size = Nstudies), alpha = .7) +
  geom_tiplab2(aes(alpha = hasN), offset = 3, size = 2.5) +
  # tweak scales
  scale_alpha_continuous(range = c(.2, 1)) +
  scale_size_area(max_size = 8) +
  # widen plotting area
  xlim(NA, 100)

p <- rotate(p, 290)
pcol <- ggplot(tibble(cols = cols, x = 1:4), aes(x, y = 1, col = cols)) +
  geom_point(size = 6, alpha = .3) +
  scale_color_identity("Clade", guide = "legend", breaks = cols[4:1], 
                       labels = c("Hominoidea", "Cercopithecoidea", "Platyrrhini", 
                                  "Tarsiiformes & Strepsirrhini")) +
  theme_cowplot()

l1 <- get_legend(pcol)
psize <- ggplot(data2, aes(size = Nstudies, x = 1, y = 1)) +
  geom_point(alpha = .7) +
  scale_size_area("Number of Studies", max_size = 8, breaks = c(1, 5, 10, 25, 50, 100)) +
  theme_cowplot()

l2 <- get_legend(psize)
px <- plot_grid(p, plot_grid(NA, l1, l2, NA, ncol = 1, rel_heights = c(.3, .15, .15, .3)), NA,
          nrow = 1, rel_widths = c(1, .2, .1))
Cannot convert object of class logical into a grob.Cannot convert object of class logical into a grob.Removed 217 rows containing missing values (geom_point_g_gtree).Cannot convert object of class logical into a grob.
px

ggsave("../graphs/phylo_full.pdf", px, width = 7.5, height = 5.5, scale = 2)
ggsave("../graphs/phylo_full.png", width = 7.5, height = 5.5, scale = 2)
ggsave("../graphs/phylo_full.tiff", width = 7.5, height = 5.5, scale = 2, type = "cairo", 
       compression = "lzw")
# to figure out node numbers
n1 <- p + geom_text(aes(label = node, x = branch), size = 2, col = "blue", vjust = -.5)
ggsave("../graphs/full_tree_nodes_circular.pdf", n1, width = 8, height = 8, scale = 2)
n2 <- ggtree(tree4, aes(size = hasN, alpha = hasN2)) +
  # highlight clades with background colors
  geom_hilight(node = 474, fill = cols[1], alpha = .3) +
  geom_hilight(node = 477, fill = cols[1], alpha = .3) +
  geom_hilight(node = 411, fill = cols[2], alpha = .3) +
  geom_hilight(node = 291, fill = cols[3], alpha = .3) +
  geom_hilight(node = 395, fill = cols[4], alpha = .3) +
  # plot tree again to be on top of the highlights
  geom_tree() +
  # root
  geom_rootedge(rootedge = 2) +
  geom_rootpoint(size = 1) +
  # node labels
  geom_text(aes(label = node, x = branch), size = 2, col = "blue", vjust = -.5) +
  # tips
  geom_tippoint(aes(size = Nstudies), alpha = .7) +
  geom_tiplab(aes(alpha = hasN), offset = 1.8, size = 3) +
  # tweak scales
  scale_alpha_continuous(range = c(.2, 1)) +
  scale_size_continuous(range = c(.5, 8)) +
  # widen plotting area
  expand_limits(x = 90) +
  theme_tree2()

ggsave("../graphs/full_tree_nodes.pdf", n2, width = 8, height = 20, scale = 2)

Sample size in detail

studies
# subset tree to just those species who have sample sizes reported, i.e. those who were tested
to_drop <- tree3 %>% filter(is.na(Nstudies)) %>% pull(label)
to_drop2 <- data %>% group_by(label2) %>% filter(all(is.na(num))) %>% pull(label2) %>% str_replace_all('_', ' ')
tree5 <- drop.tip(tree4, c(to_drop, to_drop2))
d3 <- data %>% 
  mutate(label = str_replace_all(label2, "_", " ")) %>% 
  group_by(label, species, studyID) %>% 
  summarise(num = sum(num))
d3 %>% arrange(desc(num))
# filter super large samples out for visualization? note in caption
# species with more than X studies can get a density
d3a <- d3 %>% group_by(species) %>% filter(n_distinct(studyID) >= 4, num <= 200)
d3b <- d3 %>% # setdiff(d3, d3a) %>% ## <- to NOT show points for densities
  group_by(species) %>% 
  # create variable num2 is NA if there's only one data point for a species
  # --> those species will only get the vertical crossbar
  mutate(flag = n_distinct(studyID) == 1) %>% 
  ungroup %>% 
  mutate(num2 = ifelse(flag, NA, num)) %>% 
  filter(num <= 200)

# for vertical crossbar = median
d4 <- d3 %>% 
  group_by(label, species) %>% 
  summarise(Mdn = median(num, na.rm = T)) # totalN = sum(num), sitesN = n_distinct(site)

# for vertical line in ridge plot (grand median)
# + hacky way to make horizontal grid lines for right panel only
v <- tibble(reference = c(NA, median(d3$num, na.rm = T)), .panel = c("Tree", "xSample size"))
h <- tibble(reference = c(NA, 1:Ntip(tree5)), .panel = c("Tree", rep("xSample size", Ntip(tree5))))

# for axis labels
ax <- tibble(lab = c("Distance (Millions of years)", "Sample size"), 
             x = c(60, 100), y = -4, .panel = c("Tree", "xSample size"))

# Nsites/studies labels
Nlab <- tibble(lab = c("# Sites", "# Studies"), x = c(125, 136), y = Ntip(tree5) + 1, 
             .panel = "Tree")
# LEFT FACET
q <- ggtree(tree5, aes(col = group)) +
  # root
  geom_rootedge(rootedge = 5) +
  # tip labels
  geom_tippoint(aes(size = Nstudies), shape = 21, fill = "white") +
  geom_tippoint(aes(size = Nsites), stroke = 0, alpha = .8) +
  # geom_tiplab(aes(label = str_c(label, " (", Nsites, "/", Nstudies, ")")), offset = 4, size = 3) +
  geom_tiplab(offset = 4, size = 3) +
  geom_text(aes(label = Nsites), x = 135, hjust = 1, size = 3) +
  geom_text(aes(label = Nstudies), x = 142, hjust = 1, size = 3) +
  # tweak scales
  scale_color_manual(values = c("grey30", cols)) +
  scale_fill_manual(values = cols) +
  scale_size_area(max_size = 8) +
  # display timescale at the bottom
  theme_tree2() +
  xlim_tree(142) +
  xlim_expand(c(0, 175), "xSample size") +
  # # node labels if needed for reference
  # geom_text(aes(label = node, x = branch), size = 2, col = "blue", vjust = -.5) +
  # add axis + Nstudies/sites labels
  geom_text(data = ax, aes(label = lab), col = "black") +
  geom_text(data = Nlab, aes(label = lab), col = "black", size = 2.5) +
  scale_x_continuous(expand = expand_scale(mult = c(0, .01))) +
  scale_y_continuous(limits = c(2, Ntip(tree5)-1), oob = function(x, ...) x) +
  coord_cartesian(clip = "off") +
  # add reference lines (these will show up on right panel of facet_plot only)
  geom_hline(data = h, aes(yintercept = reference), lwd = .2, col = "grey", alpha = .5) +
  geom_vline(data = v, aes(xintercept = reference), lwd = 1.5, col = "grey", alpha = .3) +
  # remove facet strips, expand bottom margin (to make space for x axis labels)
  theme(strip.text = element_blank(), strip.background = element_blank(),
        plot.margin = unit(c(1, 1, 2, 1.5), "cm"), panel.spacing = unit(1, "cm"))

q <- rotate(q, 71)
# right-side viz depends on the number of sites per species:
# 1 site = vertical crossbar only
# 2+ sites = points + crossbar at median
# X+ sites = densities (currently, X = 4 just to illustrate)

# dirty hack: x in front of "Sample size" is to have that panel sort to the right (alphabetically) until I figure out why it doesn't just go by order. This cropped up as an issue when I added the dummy point for the x-axis expansion...

# ADD RIGHT FACET
qx <- q %>% 
  # densities for species with enough sites
  facet_plot("xSample size", d3a, geom_density_ridges, 
             aes(x = num, group = label, fill = group, height = ..density..),
             alpha = .5, lwd = .3, scale = .3) %>%
  # vertical crossbar for Mdn
  facet_plot("xSample size", d4, geom_crossbarh, aes(x = Mdn, xmin = Mdn, xmax = Mdn, group = label,
             col = group), alpha = .5, width = .6, fatten = 1.5) %>%
  # vertical mark for individual sites
  facet_plot("xSample size", d3b, geom_jitter, aes(x = num2, group = label), shape = "|", size = 2.5,
             width = .5, height = 0, alpha = .5)
# add legends
psize <-
  ggplot(data2, aes(x = 1, y = 1)) +
  geom_point(aes(size = Nstudies), col = NA) +
  geom_point(aes(size = Nsites), stroke = 0, alpha = .8) +
  scale_size_area("Number of Sites", max_size = 8, breaks = c(1, 5, 10, 25, 50)) +
  theme_cowplot()

psize2 <-
  ggplot(data2, aes(x = 1, y = 1)) +
  geom_point(aes(size = Nstudies), shape = 21, fill = "white") +
  scale_size_area("\nNumber of Studies", max_size = 8, breaks = c(1, 5, 10, 25, 50, 100)) +
  theme_cowplot()

l2 <- get_legend(psize)
Removed 69 rows containing missing values (geom_point).
l3 <- get_legend(psize2)
qx2 <- plot_grid(qx, plot_grid(NA, l1, l2, l3, NA, ncol = 1, rel_heights = c(.3, .1, .1, .1, .3)), NA,
          nrow = 1, rel_widths = c(1, .2, .1))
Cannot convert object of class logical into a grob.Cannot convert object of class logical into a grob.Picking joint bandwidth of NaN
no non-missing arguments to min; returning Infno non-missing arguments to max; returning -InfPicking joint bandwidth of 4.06
no non-missing arguments to max; returning -InfRemoved 66 rows containing missing values (geom_text).Removed 66 rows containing missing values (geom_text).Removed 1 rows containing missing values (geom_hline).Removed 1 rows containing missing values (geom_vline).Removed 32 rows containing missing values (geom_point).Cannot convert object of class logical into a grob.
qx2

ggsave("../graphs/phylo_ridge_site.pdf", width = 8, height = 8, scale = 2)
ggsave("../graphs/phylo_ridge_site.png", width = 8, height = 8, scale = 2)
ggsave("../graphs/phylo_ridge_site.tiff", width = 8, height = 8, scale = 2, type = "cairo", 
       compression = "lzw")

Diversity score

# subset tree to just those species who have sample sizes reported, i.e. those who were tested
to_drop <- tree3 %>% filter(is.na(Nstudies) | Nstudies < 2) %>% pull(label)
tree6 <- drop.tip(tree4, to_drop)
ggtree(tree6, aes(col = group)) +
  # root
  geom_rootedge(rootedge = 5) +
  # tip labels
  geom_tippoint(aes(size = Nstudies), shape = 21, fill = "white") +
  geom_tippoint(aes(size = Nsites), stroke = 0, alpha = .8) +
  geom_tiplab(offset = 4, size = 3) +
  geom_text(aes(label = Nsites), x = 113, hjust = 1, size = 3) +
  geom_text(aes(label = Nstudies), x = 120, hjust = 1, size = 3) +
  # tweak scales
  scale_color_manual(values = c("grey30", cols)) +
  scale_fill_manual(values = cols) +
  scale_size_area(max_size = 8) +
  # display timescale at the bottom
  theme_tree2() +
  xlim_tree(120) +
  xlab("Distance (Millions of years)")
# ggsave("../graphs/phylo_div_score.pdf", width = 4, height = 4.5, scale = 2)

Session info

sessionInfo()
R version 3.6.1 (2019-07-05)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS Mojave 10.14.5

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/3.6/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] tidytree_0.2.8    ggtree_1.16.6     treeio_1.8.2      ggstance_0.3.3    ggridges_0.5.1   
 [6] viridis_0.5.1     viridisLite_0.3.0 cowplot_1.0.0     gridExtra_2.3     forcats_0.4.0    
[11] stringr_1.4.0     dplyr_0.8.3       purrr_0.3.2       readr_1.3.1       tidyr_1.0.0      
[16] tibble_2.1.3      ggplot2_3.2.1     tidyverse_1.2.1  

loaded via a namespace (and not attached):
 [1] tidyselect_0.2.5   xfun_0.10          reshape2_1.4.3     haven_2.1.1        lattice_0.20-38   
 [6] colorspace_1.4-1   vctrs_0.2.0        generics_0.0.2     rlang_0.4.0        pillar_1.4.2      
[11] glue_1.3.1         withr_2.1.2        modelr_0.1.5       readxl_1.3.1       rvcheck_0.1.5     
[16] lifecycle_0.1.0    plyr_1.8.4         munsell_0.5.0      gtable_0.3.0       cellranger_1.1.0  
[21] rvest_0.3.4        labeling_0.3       knitr_1.25         parallel_3.6.1     broom_0.5.2       
[26] Rcpp_1.0.2         BiocManager_1.30.7 scales_1.0.0       backports_1.1.5    jsonlite_1.6      
[31] digest_0.6.21      hms_0.5.1          stringi_1.4.3      grid_3.6.1         cli_1.1.0         
[36] tools_3.6.1        magrittr_1.5       lazyeval_0.2.2     crayon_1.3.4       ape_5.3           
[41] pkgconfig_2.0.3    zeallot_0.1.0      xml2_1.2.2         lubridate_1.7.4    assertthat_0.2.1  
[46] httr_1.4.1         rstudioapi_0.10    R6_2.4.0           nlme_3.1-141       compiler_3.6.1    
LS0tCnRpdGxlOiAiUGh5bG9nZW5ldGljIFRyZWUiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIGNzczogc3R5bGUuY3NzCiAgICB0aGVtZTogcGFwZXIKLS0tCgpMb2FkIHBhY2thZ2VzCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkodmlyaWRpcykKbGlicmFyeShnZ3JpZGdlcykKbGlicmFyeShnZ3N0YW5jZSkKbGlicmFyeSh0cmVlaW8pCmxpYnJhcnkoZ2d0cmVlKQpsaWJyYXJ5KHRpZHl0cmVlKQpgYGAKCkxvYWQgYW5kIGNvbWJpbmUgZGF0YQoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CnN0dWRpZXMgPC0gcmVhZF9jc3YoIi4uL2RhdGEvc3R1ZGllc19nc2hlZXQuY3N2IikKc3BlY2llcyA8LSByZWFkX2NzdigiLi4vZGF0YS9zcGVjaWVzX2dzaGVldC5jc3YiKQpmdWxsdHJlZSA8LSByZWFkLm5leHVzKCIuLi9kYXRhL2NvbnNlbnN1c1RyZWVfMTBrVHJlZXNfMjk4UHJpbWF0ZXNfVjMubmV4IikKcmVmcyA8LSByZWFkX2NzdigiLi4vZGF0YS9yZWZfbm9kZXMuY3N2IikKYGBgCgpgYGB7cn0KZGF0YSA8LSBzdHVkaWVzICU+JSAKICBsZWZ0X2pvaW4oc3BlY2llcywgYnkgPSBjKCJzcGVjaWVzIiA9ICJzcGVjaWVzX2ZpbGUiKSkgJT4lIAogIHJlbmFtZShsYWJlbCA9IHNwZWNpZXNfdHJlZSwgbGFiZWwyID0gdXBkYXRlZF9ub21lbmNsYXR1cmUsIG51bSA9IG4pICU+JSAKICBtdXRhdGUobGFiZWwyID0gaWZlbHNlKGlzLm5hKGxhYmVsMiksIGxhYmVsLCBsYWJlbDIpLAogICAgICAgICBsYWJlbDIgPSBmY3RfcmVjb2RlKGxhYmVsMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU2FwYWp1c19zcHAuIiA9ICJTYXBhanVzX2FwZWxsYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkhvb2xvY2tfbGV1Y29uZWR5cyoiID0gIkhvb2xvY2tfaG9vbG9jayIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBsZWN0dXJvY2VidXNfY3VwcmV1cyoiID0gIlBsZWN0dXJvY2VidXNfbW9sb2NoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2FsbGljZWJ1c19uaWdyaWZyb25zKiIgPSAiQ2FsbGljZWJ1c19wZXJzb25hdHVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRXVsZW11cl9ydWZpZnJvbnMqIiA9ICJFdWxlbXVyX3J1ZnVzIikpICU+JSAKICBzZWxlY3QobGFiZWwsIGxhYmVsMiwgc3R1ZHlJRCwgc3BlY2llcywgc2l0ZSwgbnVtKQpgYGAKCmBgYHtyfQpkYXRhMiA8LSBkYXRhICU+JQogIGdyb3VwX2J5KGxhYmVsLCBsYWJlbDIsIHNwZWNpZXMpICU+JSAKICBzdW1tYXJpc2UoTnNpdGVzID0gbl9kaXN0aW5jdChzaXRlKSwgTnN0dWRpZXMgPSBuX2Rpc3RpbmN0KHN0dWR5SUQpKSAlPiUgCiAgbXV0YXRlKGRpdl9zY29yZSA9IE5zaXRlcy9Oc3R1ZGllcykKYGBgCgpgYGB7cn0Kc3BlY2llcyA8LSBzcGVjaWVzICU+JSAKICBtdXRhdGUodXBkYXRlZF9ub21lbmNsYXR1cmUgPSBmY3RfcmVjb2RlKHVwZGF0ZWRfbm9tZW5jbGF0dXJlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNhcGFqdXNfc3BwLiIgPSAiU2FwYWp1c19hcGVsbGEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkhvb2xvY2tfbGV1Y29uZWR5cyoiID0gIkhvb2xvY2tfaG9vbG9jayIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUGxlY3R1cm9jZWJ1c19jdXByZXVzKiIgPSAiUGxlY3R1cm9jZWJ1c19tb2xvY2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNhbGxpY2VidXNfbmlncmlmcm9ucyoiID0gIkNhbGxpY2VidXNfcGVyc29uYXR1cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRXVsZW11cl9ydWZpZnJvbnMqIiA9ICJFdWxlbXVyX3J1ZnVzIikpCmBgYAoKYGBge3J9CiMgdHVybiB0cmVlIGludG8gdGlkeSBkYXRhZnJhbWUKdHJlZTIgPC0gZnVsbHRyZWUgJT4lIAogIGRyb3AudGlwKGMoIlBhbl90cm9nbG9keXRlc19zY2h3ZWluZnVydGhpaSIsICJQYW5fdHJvZ2xvZHl0ZXNfdHJvZ2xvZHl0ZXMiLAogICAgICAgICAgICAgIlBhbl90cm9nbG9keXRlc192ZWxsZXJvc3VzIiwgIlBvbmdvX3B5Z21hZXVzIiwgIkNlcmNvcGl0aGVjdXNfY2VwaHVzX2NlcGh1cyIsCiAgICAgICAgICAgICAiQ2VyY29waXRoZWN1c19lcnl0aHJvZ2FzdGVyX2VyeXRocm9nYXN0ZXIiLCAiRXVsZW11cl9mdWx2dXNfbWF5b3R0ZW5zaXMiLAogICAgICAgICAgICAgIkhhcGFsZW11cl9ncmlzZXVzX2dyaXNldXMiLCAiTWljcm9jZWJ1c19sb2tvYmVuc2lzIiwgIkxlcGlsZW11cl9taXRzaW5qb2Vuc2lzIiwKICAgICAgICAgICAgICJHb3JpbGxhX2dvcmlsbGFfZ3JhdWVyaSIsICJDZWJ1c194YW50aG9zdGVybm9zIikpICU+JQogIGFzX3RpYmJsZQoKaW5uZXJfbm9kZXMgPC0gYygyODc6Mjk2LCAzMDU6MzA2LCAzMTE6MzE3LCAzMTk6MzIzLCAzMjUsIDMyNzozNDQsIDM0NzozNDksIDM1MzozNTUsIDM1NywgMzczOjM4MSwgMzg2LCAzOTA6MzkzLCAzOTU6NDAxLCA0MDM6NDEyLCA0MjA6NDIzLCA0MjU6NDI5LCA0MzU6NDM5LCA0NDM6NDQ1LCA0NDc6NDUxLCA0NTQ6NDYxLCA0NjM6NDY3LCA0Njk6NDczLCA0Nzc6NDg4LCA1MDAsIDUxODo1MTksIDUyMzo1MjUsIDUyODo1MzIsIDUzODo1NDAsIDU0Njo1NDgsIDU1MSwgNTUzKQoKdHJlZTMgPC0gdHJlZTIgJT4lIAogIG11dGF0ZShsYWJlbCA9IGZjdF9yZWNvZGUobGFiZWwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBvbmdvX3NwcC4iID0gIlBvbmdvX2FiZWxpaSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUGFuX3Ryb2dsb2R5dGVzIiA9ICJQYW5fdHJvZ2xvZHl0ZXNfdmVydXMiKSkgJT4lIAogIGxlZnRfam9pbihkYXRhMikgJT4lIAogIGxlZnRfam9pbihzcGVjaWVzLCBieSA9IGMoImxhYmVsIiA9ICJzcGVjaWVzX3RyZWUiKSkgJT4lIAogIHNlbGVjdCgtbGFiZWwsIC1sYWJlbDIsIC1zcGVjaWVzX2ZpbGUpICU+JQogIHJlbmFtZShsYWJlbCA9IHVwZGF0ZWRfbm9tZW5jbGF0dXJlKSAlPiUKICBtdXRhdGUoCiAgICBoYXNOID0gaWZlbHNlKGlzLm5hKE5zdHVkaWVzKSwgLjEsIC41KSwgIyB1c2VkIHRvIHNpemUgYnJhbmNoZXMgKyBjb2xvciB0aGUgdGlwIGxhYmVscwogICAgaGFzTjIgPSBpZmVsc2UoaXMubmEoTnN0dWRpZXMpICYgIShub2RlICVpbiUgaW5uZXJfbm9kZXMpLCAwLCAuNSksICMgdXNlZCB0byBjb2xvciBicmFuY2hlcwogICAgbGFiZWwgPSBzdHJfcmVwbGFjZV9hbGwobGFiZWwsICJfIiwgIiAiKSwKICAgICMgbGFiZWwgPSBmY3RfcmVjb2RlKGxhYmVsLCAiU2FwYWp1cyBzcHAuIiA9ICJTYXBhanVzIGFwZWxsYSIsICJIb29sb2NrIGhvb2xvY2sqIiA9ICJIb29sb2NrIGhvb2xvY2siKSwKICAgIGxhYmVsID0gaWZlbHNlKGlzLm5hKGxhYmVsKSAmIHNwZWNpZXMgPT0gIm9yYW5ndXRhbiIsICJQb25nbyBzcHAuIiwgCiAgICAgICAgICAgICAgaWZlbHNlKGlzLm5hKGxhYmVsKSAmIHNwZWNpZXMgPT0gImNoaW1wYW56ZWUiLCAiUGFuIHRyb2dsb2R5dGVzIiwgbGFiZWwpKSkgJT4lIAogIGxlZnRfam9pbihyZWZzKSAlPiUgCiAgZ3JvdXBDbGFkZShyZWZzJG5vZGVbLTFdKSAlPiUgCiAgbXV0YXRlKGdyb3VwID0gZmN0X3JlY29kZShncm91cCwgIjIiID0gIjEiKSkKCiMgdHVybiBiYWNrIGludG8gdHJlZQp0cmVlNCA8LSBhcy50cmVlZGF0YSh0cmVlMykKYGBgCgojIENpcmN1bGFyIHRyZWUgb2YgdGhlIDEwa3RyZWUgcHJpbWF0ZXMKCmBgYHtyfQpjb2xzIDwtIHZpcmlkaXMoNCwgZW5kID0gLjkpCmBgYAoKYGBge3J9CnAgPC0gZ2d0cmVlKHRyZWU0LCBhZXMoYWxwaGEgPSBoYXNOMiksIGxheW91dCA9ICJjaXJjdWxhciIpICsgIyBzaXplID0gaGFzTiwgCiAgIyBoaWdobGlnaHQgY2xhZGVzIHdpdGggYmFja2dyb3VuZCBjb2xvcnMKICBnZW9tX2hpbGlnaHQobm9kZSA9IDQ3NCwgZmlsbCA9IGNvbHNbMV0sIGFscGhhID0gLjMpICsKICBnZW9tX2hpbGlnaHQobm9kZSA9IDQ3NywgZmlsbCA9IGNvbHNbMV0sIGFscGhhID0gLjMpICsKICBnZW9tX2hpbGlnaHQobm9kZSA9IDQxMSwgZmlsbCA9IGNvbHNbMl0sIGFscGhhID0gLjMpICsKICBnZW9tX2hpbGlnaHQobm9kZSA9IDI5MSwgZmlsbCA9IGNvbHNbM10sIGFscGhhID0gLjMpICsKICBnZW9tX2hpbGlnaHQobm9kZSA9IDM5NSwgZmlsbCA9IGNvbHNbNF0sIGFscGhhID0gLjMpICsKICAjIHBsb3QgdHJlZSBhZ2FpbiB0byBiZSBvbiB0b3Agb2YgdGhlIGhpZ2hsaWdodHMKICBnZW9tX3RyZWUoKSArCiAgIyByb290CiAgZ2VvbV9yb290cG9pbnQoc2l6ZSA9IDEpICsKICAjIHRpcHMKICBnZW9tX3RpcHBvaW50KGFlcyhzaXplID0gTnN0dWRpZXMpLCBhbHBoYSA9IC43KSArCiAgZ2VvbV90aXBsYWIyKGFlcyhhbHBoYSA9IGhhc04pLCBvZmZzZXQgPSAzLCBzaXplID0gMi41KSArCiAgIyB0d2VhayBzY2FsZXMKICBzY2FsZV9hbHBoYV9jb250aW51b3VzKHJhbmdlID0gYyguMiwgMSkpICsKICBzY2FsZV9zaXplX2FyZWEobWF4X3NpemUgPSA4KSArCiAgIyB3aWRlbiBwbG90dGluZyBhcmVhCiAgeGxpbShOQSwgMTAwKQoKcCA8LSByb3RhdGUocCwgMjkwKQpgYGAKCmBgYHtyfQpwY29sIDwtIGdncGxvdCh0aWJibGUoY29scyA9IGNvbHMsIHggPSAxOjQpLCBhZXMoeCwgeSA9IDEsIGNvbCA9IGNvbHMpKSArCiAgZ2VvbV9wb2ludChzaXplID0gNiwgYWxwaGEgPSAuMykgKwogIHNjYWxlX2NvbG9yX2lkZW50aXR5KCJDbGFkZSIsIGd1aWRlID0gImxlZ2VuZCIsIGJyZWFrcyA9IGNvbHNbNDoxXSwgCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiSG9taW5vaWRlYSIsICJDZXJjb3BpdGhlY29pZGVhIiwgIlBsYXR5cnJoaW5pIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVGFyc2lpZm9ybWVzICYgU3RyZXBzaXJyaGluaSIpKSArCiAgdGhlbWVfY293cGxvdCgpCgpsMSA8LSBnZXRfbGVnZW5kKHBjb2wpCmBgYAoKYGBge3J9CnBzaXplIDwtIGdncGxvdChkYXRhMiwgYWVzKHNpemUgPSBOc3R1ZGllcywgeCA9IDEsIHkgPSAxKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAuNykgKwogIHNjYWxlX3NpemVfYXJlYSgiTnVtYmVyIG9mIFN0dWRpZXMiLCBtYXhfc2l6ZSA9IDgsIGJyZWFrcyA9IGMoMSwgNSwgMTAsIDI1LCA1MCwgMTAwKSkgKwogIHRoZW1lX2Nvd3Bsb3QoKQoKbDIgPC0gZ2V0X2xlZ2VuZChwc2l6ZSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTcuNSwgZmlnLmhlaWdodD01LjUsIGNhY2hlPVRSVUV9CnB4IDwtIHBsb3RfZ3JpZChwLCBwbG90X2dyaWQoTkEsIGwxLCBsMiwgTkEsIG5jb2wgPSAxLCByZWxfaGVpZ2h0cyA9IGMoLjMsIC4xNSwgLjE1LCAuMykpLCBOQSwKICAgICAgICAgIG5yb3cgPSAxLCByZWxfd2lkdGhzID0gYygxLCAuMiwgLjEpKQoKcHgKYGBgCgpgYGB7ciwgY2FjaGU9VFJVRX0KZ2dzYXZlKCIuLi9ncmFwaHMvcGh5bG9fZnVsbC5wZGYiLCBweCwgd2lkdGggPSA3LjUsIGhlaWdodCA9IDUuNSwgc2NhbGUgPSAyKQpnZ3NhdmUoIi4uL2dyYXBocy9waHlsb19mdWxsLnBuZyIsIHdpZHRoID0gNy41LCBoZWlnaHQgPSA1LjUsIHNjYWxlID0gMikKZ2dzYXZlKCIuLi9ncmFwaHMvcGh5bG9fZnVsbC50aWZmIiwgd2lkdGggPSA3LjUsIGhlaWdodCA9IDUuNSwgc2NhbGUgPSAyLCB0eXBlID0gImNhaXJvIiwgCiAgICAgICBjb21wcmVzc2lvbiA9ICJsenciKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04LCBjYWNoZT1UUlVFLCBldmFsPUZBTFNFfQojIHRvIGZpZ3VyZSBvdXQgbm9kZSBudW1iZXJzCm4xIDwtIHAgKyBnZW9tX3RleHQoYWVzKGxhYmVsID0gbm9kZSwgeCA9IGJyYW5jaCksIHNpemUgPSAyLCBjb2wgPSAiYmx1ZSIsIHZqdXN0ID0gLS41KQpnZ3NhdmUoIi4uL2dyYXBocy9mdWxsX3RyZWVfbm9kZXNfY2lyY3VsYXIucGRmIiwgbjEsIHdpZHRoID0gOCwgaGVpZ2h0ID0gOCwgc2NhbGUgPSAyKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD0yMCwgY2FjaGU9VFJVRSwgZXZhbD1GQUxTRX0KbjIgPC0gZ2d0cmVlKHRyZWU0LCBhZXMoc2l6ZSA9IGhhc04sIGFscGhhID0gaGFzTjIpKSArCiAgIyBoaWdobGlnaHQgY2xhZGVzIHdpdGggYmFja2dyb3VuZCBjb2xvcnMKICBnZW9tX2hpbGlnaHQobm9kZSA9IDQ3NCwgZmlsbCA9IGNvbHNbMV0sIGFscGhhID0gLjMpICsKICBnZW9tX2hpbGlnaHQobm9kZSA9IDQ3NywgZmlsbCA9IGNvbHNbMV0sIGFscGhhID0gLjMpICsKICBnZW9tX2hpbGlnaHQobm9kZSA9IDQxMSwgZmlsbCA9IGNvbHNbMl0sIGFscGhhID0gLjMpICsKICBnZW9tX2hpbGlnaHQobm9kZSA9IDI5MSwgZmlsbCA9IGNvbHNbM10sIGFscGhhID0gLjMpICsKICBnZW9tX2hpbGlnaHQobm9kZSA9IDM5NSwgZmlsbCA9IGNvbHNbNF0sIGFscGhhID0gLjMpICsKICAjIHBsb3QgdHJlZSBhZ2FpbiB0byBiZSBvbiB0b3Agb2YgdGhlIGhpZ2hsaWdodHMKICBnZW9tX3RyZWUoKSArCiAgIyByb290CiAgZ2VvbV9yb290ZWRnZShyb290ZWRnZSA9IDIpICsKICBnZW9tX3Jvb3Rwb2ludChzaXplID0gMSkgKwogICMgbm9kZSBsYWJlbHMKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gbm9kZSwgeCA9IGJyYW5jaCksIHNpemUgPSAyLCBjb2wgPSAiYmx1ZSIsIHZqdXN0ID0gLS41KSArCiAgIyB0aXBzCiAgZ2VvbV90aXBwb2ludChhZXMoc2l6ZSA9IE5zdHVkaWVzKSwgYWxwaGEgPSAuNykgKwogIGdlb21fdGlwbGFiKGFlcyhhbHBoYSA9IGhhc04pLCBvZmZzZXQgPSAxLjgsIHNpemUgPSAzKSArCiAgIyB0d2VhayBzY2FsZXMKICBzY2FsZV9hbHBoYV9jb250aW51b3VzKHJhbmdlID0gYyguMiwgMSkpICsKICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKC41LCA4KSkgKwogICMgd2lkZW4gcGxvdHRpbmcgYXJlYQogIGV4cGFuZF9saW1pdHMoeCA9IDkwKSArCiAgdGhlbWVfdHJlZTIoKQoKZ2dzYXZlKCIuLi9ncmFwaHMvZnVsbF90cmVlX25vZGVzLnBkZiIsIG4yLCB3aWR0aCA9IDgsIGhlaWdodCA9IDIwLCBzY2FsZSA9IDIpCmBgYAoKCiMgU2FtcGxlIHNpemUgaW4gZGV0YWlsCgpgYGB7cn0Kc3R1ZGllcwpgYGAKCmBgYHtyfQojIHN1YnNldCB0cmVlIHRvIGp1c3QgdGhvc2Ugc3BlY2llcyB3aG8gaGF2ZSBzYW1wbGUgc2l6ZXMgcmVwb3J0ZWQsIGkuZS4gdGhvc2Ugd2hvIHdlcmUgdGVzdGVkCnRvX2Ryb3AgPC0gdHJlZTMgJT4lIGZpbHRlcihpcy5uYShOc3R1ZGllcykpICU+JSBwdWxsKGxhYmVsKQp0b19kcm9wMiA8LSBkYXRhICU+JSBncm91cF9ieShsYWJlbDIpICU+JSBmaWx0ZXIoYWxsKGlzLm5hKG51bSkpKSAlPiUgcHVsbChsYWJlbDIpICU+JSBzdHJfcmVwbGFjZV9hbGwoJ18nLCAnICcpCnRyZWU1IDwtIGRyb3AudGlwKHRyZWU0LCBjKHRvX2Ryb3AsIHRvX2Ryb3AyKSkKZDMgPC0gZGF0YSAlPiUgCiAgbXV0YXRlKGxhYmVsID0gc3RyX3JlcGxhY2VfYWxsKGxhYmVsMiwgIl8iLCAiICIpKSAlPiUgCiAgZ3JvdXBfYnkobGFiZWwsIHNwZWNpZXMsIHN0dWR5SUQpICU+JSAKICBzdW1tYXJpc2UobnVtID0gc3VtKG51bSkpCmBgYAoKYGBge3J9CmQzICU+JSBhcnJhbmdlKGRlc2MobnVtKSkKYGBgCgpgYGB7cn0KIyBmaWx0ZXIgc3VwZXIgbGFyZ2Ugc2FtcGxlcyBvdXQgZm9yIHZpc3VhbGl6YXRpb24/IG5vdGUgaW4gY2FwdGlvbgojIHNwZWNpZXMgd2l0aCBtb3JlIHRoYW4gWCBzdHVkaWVzIGNhbiBnZXQgYSBkZW5zaXR5CmQzYSA8LSBkMyAlPiUgZ3JvdXBfYnkoc3BlY2llcykgJT4lIGZpbHRlcihuX2Rpc3RpbmN0KHN0dWR5SUQpID49IDQsIG51bSA8PSAyMDApCmQzYiA8LSBkMyAlPiUgIyBzZXRkaWZmKGQzLCBkM2EpICU+JSAjIyA8LSB0byBOT1Qgc2hvdyBwb2ludHMgZm9yIGRlbnNpdGllcwogIGdyb3VwX2J5KHNwZWNpZXMpICU+JSAKICAjIGNyZWF0ZSB2YXJpYWJsZSBudW0yIGlzIE5BIGlmIHRoZXJlJ3Mgb25seSBvbmUgZGF0YSBwb2ludCBmb3IgYSBzcGVjaWVzCiAgIyAtLT4gdGhvc2Ugc3BlY2llcyB3aWxsIG9ubHkgZ2V0IHRoZSB2ZXJ0aWNhbCBjcm9zc2JhcgogIG11dGF0ZShmbGFnID0gbl9kaXN0aW5jdChzdHVkeUlEKSA9PSAxKSAlPiUgCiAgdW5ncm91cCAlPiUgCiAgbXV0YXRlKG51bTIgPSBpZmVsc2UoZmxhZywgTkEsIG51bSkpICU+JSAKICBmaWx0ZXIobnVtIDw9IDIwMCkKCiMgZm9yIHZlcnRpY2FsIGNyb3NzYmFyID0gbWVkaWFuCmQ0IDwtIGQzICU+JSAKICBncm91cF9ieShsYWJlbCwgc3BlY2llcykgJT4lIAogIHN1bW1hcmlzZShNZG4gPSBtZWRpYW4obnVtLCBuYS5ybSA9IFQpKSAjIHRvdGFsTiA9IHN1bShudW0pLCBzaXRlc04gPSBuX2Rpc3RpbmN0KHNpdGUpCgojIGZvciB2ZXJ0aWNhbCBsaW5lIGluIHJpZGdlIHBsb3QgKGdyYW5kIG1lZGlhbikKIyArIGhhY2t5IHdheSB0byBtYWtlIGhvcml6b250YWwgZ3JpZCBsaW5lcyBmb3IgcmlnaHQgcGFuZWwgb25seQp2IDwtIHRpYmJsZShyZWZlcmVuY2UgPSBjKE5BLCBtZWRpYW4oZDMkbnVtLCBuYS5ybSA9IFQpKSwgLnBhbmVsID0gYygiVHJlZSIsICJ4U2FtcGxlIHNpemUiKSkKaCA8LSB0aWJibGUocmVmZXJlbmNlID0gYyhOQSwgMTpOdGlwKHRyZWU1KSksIC5wYW5lbCA9IGMoIlRyZWUiLCByZXAoInhTYW1wbGUgc2l6ZSIsIE50aXAodHJlZTUpKSkpCgojIGZvciBheGlzIGxhYmVscwpheCA8LSB0aWJibGUobGFiID0gYygiRGlzdGFuY2UgKE1pbGxpb25zIG9mIHllYXJzKSIsICJTYW1wbGUgc2l6ZSIpLCAKICAgICAgICAgICAgIHggPSBjKDYwLCAxMDApLCB5ID0gLTQsIC5wYW5lbCA9IGMoIlRyZWUiLCAieFNhbXBsZSBzaXplIikpCgojIE5zaXRlcy9zdHVkaWVzIGxhYmVscwpObGFiIDwtIHRpYmJsZShsYWIgPSBjKCIjIFNpdGVzIiwgIiMgU3R1ZGllcyIpLCB4ID0gYygxMjUsIDEzNiksIHkgPSBOdGlwKHRyZWU1KSArIDEsIAogICAgICAgICAgICAgLnBhbmVsID0gIlRyZWUiKQpgYGAKCmBgYHtyLCBjYWNoZT1UUlVFfQojIExFRlQgRkFDRVQKcSA8LSBnZ3RyZWUodHJlZTUsIGFlcyhjb2wgPSBncm91cCkpICsKICAjIHJvb3QKICBnZW9tX3Jvb3RlZGdlKHJvb3RlZGdlID0gNSkgKwogICMgdGlwIGxhYmVscwogIGdlb21fdGlwcG9pbnQoYWVzKHNpemUgPSBOc3R1ZGllcyksIHNoYXBlID0gMjEsIGZpbGwgPSAid2hpdGUiKSArCiAgZ2VvbV90aXBwb2ludChhZXMoc2l6ZSA9IE5zaXRlcyksIHN0cm9rZSA9IDAsIGFscGhhID0gLjgpICsKICAjIGdlb21fdGlwbGFiKGFlcyhsYWJlbCA9IHN0cl9jKGxhYmVsLCAiICgiLCBOc2l0ZXMsICIvIiwgTnN0dWRpZXMsICIpIikpLCBvZmZzZXQgPSA0LCBzaXplID0gMykgKwogIGdlb21fdGlwbGFiKG9mZnNldCA9IDQsIHNpemUgPSAzKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IE5zaXRlcyksIHggPSAxMzUsIGhqdXN0ID0gMSwgc2l6ZSA9IDMpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gTnN0dWRpZXMpLCB4ID0gMTQyLCBoanVzdCA9IDEsIHNpemUgPSAzKSArCiAgIyB0d2VhayBzY2FsZXMKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiZ3JleTMwIiwgY29scykpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xzKSArCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gOCkgKwogICMgZGlzcGxheSB0aW1lc2NhbGUgYXQgdGhlIGJvdHRvbQogIHRoZW1lX3RyZWUyKCkgKwogIHhsaW1fdHJlZSgxNDIpICsKICB4bGltX2V4cGFuZChjKDAsIDE3NSksICJ4U2FtcGxlIHNpemUiKSArCiAgIyAjIG5vZGUgbGFiZWxzIGlmIG5lZWRlZCBmb3IgcmVmZXJlbmNlCiAgIyBnZW9tX3RleHQoYWVzKGxhYmVsID0gbm9kZSwgeCA9IGJyYW5jaCksIHNpemUgPSAyLCBjb2wgPSAiYmx1ZSIsIHZqdXN0ID0gLS41KSArCiAgIyBhZGQgYXhpcyArIE5zdHVkaWVzL3NpdGVzIGxhYmVscwogIGdlb21fdGV4dChkYXRhID0gYXgsIGFlcyhsYWJlbCA9IGxhYiksIGNvbCA9ICJibGFjayIpICsKICBnZW9tX3RleHQoZGF0YSA9IE5sYWIsIGFlcyhsYWJlbCA9IGxhYiksIGNvbCA9ICJibGFjayIsIHNpemUgPSAyLjUpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5kX3NjYWxlKG11bHQgPSBjKDAsIC4wMSkpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMiwgTnRpcCh0cmVlNSktMSksIG9vYiA9IGZ1bmN0aW9uKHgsIC4uLikgeCkgKwogIGNvb3JkX2NhcnRlc2lhbihjbGlwID0gIm9mZiIpICsKICAjIGFkZCByZWZlcmVuY2UgbGluZXMgKHRoZXNlIHdpbGwgc2hvdyB1cCBvbiByaWdodCBwYW5lbCBvZiBmYWNldF9wbG90IG9ubHkpCiAgZ2VvbV9obGluZShkYXRhID0gaCwgYWVzKHlpbnRlcmNlcHQgPSByZWZlcmVuY2UpLCBsd2QgPSAuMiwgY29sID0gImdyZXkiLCBhbHBoYSA9IC41KSArCiAgZ2VvbV92bGluZShkYXRhID0gdiwgYWVzKHhpbnRlcmNlcHQgPSByZWZlcmVuY2UpLCBsd2QgPSAxLjUsIGNvbCA9ICJncmV5IiwgYWxwaGEgPSAuMykgKwogICMgcmVtb3ZlIGZhY2V0IHN0cmlwcywgZXhwYW5kIGJvdHRvbSBtYXJnaW4gKHRvIG1ha2Ugc3BhY2UgZm9yIHggYXhpcyBsYWJlbHMpCiAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYygxLCAxLCAyLCAxLjUpLCAiY20iKSwgcGFuZWwuc3BhY2luZyA9IHVuaXQoMSwgImNtIikpCgpxIDwtIHJvdGF0ZShxLCA3MSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9OCwgY2FjaGU9VFJVRX0KIyByaWdodC1zaWRlIHZpeiBkZXBlbmRzIG9uIHRoZSBudW1iZXIgb2Ygc2l0ZXMgcGVyIHNwZWNpZXM6CiMgMSBzaXRlID0gdmVydGljYWwgY3Jvc3NiYXIgb25seQojIDIrIHNpdGVzID0gcG9pbnRzICsgY3Jvc3NiYXIgYXQgbWVkaWFuCiMgWCsgc2l0ZXMgPSBkZW5zaXRpZXMgKGN1cnJlbnRseSwgWCA9IDQganVzdCB0byBpbGx1c3RyYXRlKQoKIyBkaXJ0eSBoYWNrOiB4IGluIGZyb250IG9mICJTYW1wbGUgc2l6ZSIgaXMgdG8gaGF2ZSB0aGF0IHBhbmVsIHNvcnQgdG8gdGhlIHJpZ2h0IChhbHBoYWJldGljYWxseSkgdW50aWwgSSBmaWd1cmUgb3V0IHdoeSBpdCBkb2Vzbid0IGp1c3QgZ28gYnkgb3JkZXIuIFRoaXMgY3JvcHBlZCB1cCBhcyBhbiBpc3N1ZSB3aGVuIEkgYWRkZWQgdGhlIGR1bW15IHBvaW50IGZvciB0aGUgeC1heGlzIGV4cGFuc2lvbi4uLgoKIyBBREQgUklHSFQgRkFDRVQKcXggPC0gcSAlPiUgCiAgIyBkZW5zaXRpZXMgZm9yIHNwZWNpZXMgd2l0aCBlbm91Z2ggc2l0ZXMKICBmYWNldF9wbG90KCJ4U2FtcGxlIHNpemUiLCBkM2EsIGdlb21fZGVuc2l0eV9yaWRnZXMsIAogICAgICAgICAgICAgYWVzKHggPSBudW0sIGdyb3VwID0gbGFiZWwsIGZpbGwgPSBncm91cCwgaGVpZ2h0ID0gLi5kZW5zaXR5Li4pLAogICAgICAgICAgICAgYWxwaGEgPSAuNSwgbHdkID0gLjMsIHNjYWxlID0gLjMpICU+JQogICMgdmVydGljYWwgY3Jvc3NiYXIgZm9yIE1kbgogIGZhY2V0X3Bsb3QoInhTYW1wbGUgc2l6ZSIsIGQ0LCBnZW9tX2Nyb3NzYmFyaCwgYWVzKHggPSBNZG4sIHhtaW4gPSBNZG4sIHhtYXggPSBNZG4sIGdyb3VwID0gbGFiZWwsCiAgICAgICAgICAgICBjb2wgPSBncm91cCksIGFscGhhID0gLjUsIHdpZHRoID0gLjYsIGZhdHRlbiA9IDEuNSkgJT4lCiAgIyB2ZXJ0aWNhbCBtYXJrIGZvciBpbmRpdmlkdWFsIHNpdGVzCiAgZmFjZXRfcGxvdCgieFNhbXBsZSBzaXplIiwgZDNiLCBnZW9tX2ppdHRlciwgYWVzKHggPSBudW0yLCBncm91cCA9IGxhYmVsKSwgc2hhcGUgPSAifCIsIHNpemUgPSAyLjUsCiAgICAgICAgICAgICB3aWR0aCA9IC41LCBoZWlnaHQgPSAwLCBhbHBoYSA9IC41KQpgYGAKCmBgYHtyfQojIGFkZCBsZWdlbmRzCnBzaXplIDwtCiAgZ2dwbG90KGRhdGEyLCBhZXMoeCA9IDEsIHkgPSAxKSkgKwogIGdlb21fcG9pbnQoYWVzKHNpemUgPSBOc3R1ZGllcyksIGNvbCA9IE5BKSArCiAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IE5zaXRlcyksIHN0cm9rZSA9IDAsIGFscGhhID0gLjgpICsKICBzY2FsZV9zaXplX2FyZWEoIk51bWJlciBvZiBTaXRlcyIsIG1heF9zaXplID0gOCwgYnJlYWtzID0gYygxLCA1LCAxMCwgMjUsIDUwKSkgKwogIHRoZW1lX2Nvd3Bsb3QoKQoKcHNpemUyIDwtCiAgZ2dwbG90KGRhdGEyLCBhZXMoeCA9IDEsIHkgPSAxKSkgKwogIGdlb21fcG9pbnQoYWVzKHNpemUgPSBOc3R1ZGllcyksIHNoYXBlID0gMjEsIGZpbGwgPSAid2hpdGUiKSArCiAgc2NhbGVfc2l6ZV9hcmVhKCJcbk51bWJlciBvZiBTdHVkaWVzIiwgbWF4X3NpemUgPSA4LCBicmVha3MgPSBjKDEsIDUsIDEwLCAyNSwgNTAsIDEwMCkpICsKICB0aGVtZV9jb3dwbG90KCkKCmwyIDwtIGdldF9sZWdlbmQocHNpemUpCmwzIDwtIGdldF9sZWdlbmQocHNpemUyKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04LCBjYWNoZT1UUlVFfQpxeDIgPC0gcGxvdF9ncmlkKHF4LCBwbG90X2dyaWQoTkEsIGwxLCBsMiwgbDMsIE5BLCBuY29sID0gMSwgcmVsX2hlaWdodHMgPSBjKC4zLCAuMSwgLjEsIC4xLCAuMykpLCBOQSwKICAgICAgICAgIG5yb3cgPSAxLCByZWxfd2lkdGhzID0gYygxLCAuMiwgLjEpKQoKcXgyCmBgYAoKYGBge3J9Cmdnc2F2ZSgiLi4vZ3JhcGhzL3BoeWxvX3JpZGdlX3NpdGUucGRmIiwgd2lkdGggPSA4LCBoZWlnaHQgPSA4LCBzY2FsZSA9IDIpCmdnc2F2ZSgiLi4vZ3JhcGhzL3BoeWxvX3JpZGdlX3NpdGUucG5nIiwgd2lkdGggPSA4LCBoZWlnaHQgPSA4LCBzY2FsZSA9IDIpCmdnc2F2ZSgiLi4vZ3JhcGhzL3BoeWxvX3JpZGdlX3NpdGUudGlmZiIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gOCwgc2NhbGUgPSAyLCB0eXBlID0gImNhaXJvIiwgCiAgICAgICBjb21wcmVzc2lvbiA9ICJsenciKQpgYGAKCiMgRGl2ZXJzaXR5IHNjb3JlCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBzdWJzZXQgdHJlZSB0byBqdXN0IHRob3NlIHNwZWNpZXMgd2hvIGhhdmUgc2FtcGxlIHNpemVzIHJlcG9ydGVkLCBpLmUuIHRob3NlIHdobyB3ZXJlIHRlc3RlZAp0b19kcm9wIDwtIHRyZWUzICU+JSBmaWx0ZXIoaXMubmEoTnN0dWRpZXMpIHwgTnN0dWRpZXMgPCAyKSAlPiUgcHVsbChsYWJlbCkKdHJlZTYgPC0gZHJvcC50aXAodHJlZTQsIHRvX2Ryb3ApCmBgYAoKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTUsIGNhY2hlPVRSVUUsIGV2YWw9RkFMU0V9CmdndHJlZSh0cmVlNiwgYWVzKGNvbCA9IGdyb3VwKSkgKwogICMgcm9vdAogIGdlb21fcm9vdGVkZ2Uocm9vdGVkZ2UgPSA1KSArCiAgIyB0aXAgbGFiZWxzCiAgZ2VvbV90aXBwb2ludChhZXMoc2l6ZSA9IE5zdHVkaWVzKSwgc2hhcGUgPSAyMSwgZmlsbCA9ICJ3aGl0ZSIpICsKICBnZW9tX3RpcHBvaW50KGFlcyhzaXplID0gTnNpdGVzKSwgc3Ryb2tlID0gMCwgYWxwaGEgPSAuOCkgKwogIGdlb21fdGlwbGFiKG9mZnNldCA9IDQsIHNpemUgPSAzKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IE5zaXRlcyksIHggPSAxMTMsIGhqdXN0ID0gMSwgc2l6ZSA9IDMpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gTnN0dWRpZXMpLCB4ID0gMTIwLCBoanVzdCA9IDEsIHNpemUgPSAzKSArCiAgIyB0d2VhayBzY2FsZXMKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiZ3JleTMwIiwgY29scykpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xzKSArCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gOCkgKwogICMgZGlzcGxheSB0aW1lc2NhbGUgYXQgdGhlIGJvdHRvbQogIHRoZW1lX3RyZWUyKCkgKwogIHhsaW1fdHJlZSgxMjApICsKICB4bGFiKCJEaXN0YW5jZSAoTWlsbGlvbnMgb2YgeWVhcnMpIikKYGBgCgpgYGB7cn0KIyBnZ3NhdmUoIi4uL2dyYXBocy9waHlsb19kaXZfc2NvcmUucGRmIiwgd2lkdGggPSA0LCBoZWlnaHQgPSA0LjUsIHNjYWxlID0gMikKYGBgCgojIFNlc3Npb24gaW5mbwoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgo=